package com.hg.miaosha.common.aop;

import com.google.common.util.concurrent.RateLimiter;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;

@Component
@Scope
@Aspect
public class RateLimitAspect {
    private Logger log = LoggerFactory.getLogger(this.getClass());

    //用来存放不同接口的RateLimiter(key为接口名称，value为RateLimiter)
    private ConcurrentHashMap<String, RateLimiter> map = new ConcurrentHashMap<>();

    private RateLimiter rateLimiter;

    @Pointcut("@annotation(com.hg.miaosha.common.aop.RateLimit)")
    public void serviceLimit() {
    }

    @Around("serviceLimit()")
    public Object around(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
        Object obj = null;
        //获取拦截的方法名
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        //返回被织入增加处理目标对象
        Object target = joinPoint.getTarget();
        //为了获取注解信息
        String methodName = methodSignature.getName();
        Method currentMethod = target.getClass().getMethod(methodName, methodSignature.getParameterTypes());
        //获取注解信息
        RateLimit annotation = currentMethod.getAnnotation(RateLimit.class);
        int limitNum = annotation.limitNum(); //获取注解每秒加入桶中的token

        //获取rateLimiter
        if (map.containsKey(methodName)) {
            rateLimiter = map.get(methodName);
        } else {
            map.put(methodName, RateLimiter.create(limitNum));
            rateLimiter = map.get(methodName);
        }

        if (rateLimiter.tryAcquire()) {
            //执行方法
            try {
                obj = joinPoint.proceed();
            } catch (Throwable throwable) {
                throw new RuntimeException(throwable);
            }
        } else {
            //拒绝了请求（服务降级）
            throw new RuntimeException("服务繁忙");
        }

        return obj;
    }
}